﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SafeScript.MetaModel.Statements
{
    public enum AtomKind
    {
        IDENTIFIER = SafeScriptConstants.IDENTIFIER,
        TRUE = SafeScriptConstants.TRUE,
        FALSE = SafeScriptConstants.FALSE,
        STRING = SafeScriptConstants.STRING,
        INTVALUE = SafeScriptConstants.INTVALUE,
        FLOATVALUE = SafeScriptConstants.FLOATVALUE,
        SUPER = SafeScriptConstants.SUPER,
        THIS = SafeScriptConstants.THIS,
        NULL = SafeScriptConstants.NULL,
        VALUE = 10000
    }

    public class Atom : Operator
    {
        public AtomKind Kind;
        public ITypeDef Type;

        public override int GetPrecedence()
        {
            return 0;
        }

        public override ITypeDef ComputeType(ITypeDef goal, bool needsRef, ErrorReport report)
        {
            if (needsRef)
            {
                this.ReportNotAReferenceType(report);
                return null;
            }
            return this.Type;
        }
    }

    public class AtomNull : Atom
    {
        public override ITypeDef ComputeType(ITypeDef goal, bool needsRef, ErrorReport report)
        {
            if (needsRef)
            {
                this.ReportNotAReferenceType(report);
                return null;
            }

            if (goal == null)
                return base.ComputeType(goal, needsRef, report);
            if (this.Type.CanBeAssignedTo(goal))
            {
                this.Type = goal;
                return this.Type;
            }
            return base.ComputeType(goal, needsRef, report);
        }
    }

    public class AtomIdentifier : Atom
    {
        /// <summary>
        /// The name of the identifier
        /// </summary>
        public string Identifier;
        /// <summary>
        /// The resolved identifier
        /// </summary>
        public object Object;

        public override ITypeDef ComputeType(ITypeDef goal, bool needsRef, ErrorReport report)
        {
            // The identifier denotes a type?
            if (this.Object is ITypeDef)
            {
                if (needsRef)
                    this.ReportNotAReferenceType(report);
                return this.Object as ITypeDef;
            }
            //if (this.Object is Operator)
            //    return (this.Object as Operator).ComputeType(goal, needsRef, report);
            if (this.Object is VarStatement)
            {
                return (this.Object as VarStatement).Type;
            }
            // The identifier is a property?
            if (this.Object is Property)
            {
                Property prop = this.Object as Property;
                if ( needsRef && prop.Setter == null )
                    report.Add(new ErrorEntry(this.Node) { Type = ErrorType.PropertyHasNoSetter, Info = prop.Name });
                else if (!needsRef && prop.Getter == null)
                    report.Add(new ErrorEntry(this.Node) { Type = ErrorType.PropertyHasNoGetter, Info = prop.Name });

                return prop.Type;
            }
            // The identifier is a member variable?
            if (this.Object is MemberEvent)
                return this.Object as MemberEvent;
            // The identifier is a member variable?
            if (this.Object is MemberVariable)
                return (this.Object as MemberVariable).Type;
            // The identifier is a member function?
            if (this.Object is MemberFunction)
            {
                if (needsRef)
                    this.ReportNotAReferenceType(report);
                return (this.Object as MemberFunction);
            }
            // The identifier is a member function?
            if (this.Object is MemberFunctionSet)
            {
                if (needsRef)
                    this.ReportNotAReferenceType(report);
                return (this.Object as MemberFunctionSet).Type;
            }
            if (this.Object is Parameter)
            {
                return (this.Object as Parameter).Type;
            }
            if (this.Object is EnumItem)
            {
                return new InstanceType() { Type = (this.Object as EnumItem).Enum };
            }
            // TODO: This is a hack
            if (this.Object == null)
                return null;
            // TODO: Comment this back in: System.Diagnostics.Debug.Assert(false, "Unexpected kind of identifier " + this.Identifier);
            return null;
        }
    }

    public class AtomBoolValue : Atom
    {
        public bool Value;
    }

    public class AtomNumValue : Atom
    {
        public double Value;
    }

    public class AtomStringValue : Atom
    {
        public string Value;
    }
}
